home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / contributed / waste / waste 1.3 / source / welowlevelediting.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  25.1 KB  |  983 lines

  1. /*
  2.  *    WELowLevelEditing.c
  3.  *
  4.  *    WASTE PROJECT
  5.  *  Low-Level Editing Routines
  6.  *
  7.  *  Copyright (c) 1993-1998 Marco Piovanelli
  8.  *    All Rights Reserved
  9.  *
  10.  *  C port by Dan Crevier
  11.  *
  12.  */
  13.  
  14.  
  15. #include "WASTEIntf.h"
  16.  
  17. pascal Boolean _WEIsWordRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  18. {
  19.     // _WEIsWordRange returns true if the specified range is a word range,
  20.     // i.e. if it would be possible to select it by double-clicking and (optionally) dragging.
  21.  
  22.     SInt32 wordStart, wordEnd;
  23.  
  24.     // determine if rangeStart is at the beginning of a word
  25.     WEFindWord(rangeStart, kLeadingEdge, &wordStart, &wordEnd, hWE);
  26.     if (rangeStart == wordStart)
  27.     {
  28.  
  29.         // determine if rangeEnd is at the end of a word
  30.         WEFindWord(rangeEnd, kTrailingEdge, &wordStart, &wordEnd, hWE);
  31.         return (rangeEnd == wordEnd);
  32.     }
  33.     return false;
  34. }
  35.  
  36. pascal Boolean _WEIsPunct(SInt32 offset, WEHandle hWE)
  37. {
  38.     SInt16 cType;
  39.  
  40.     cType = WECharType(offset, hWE);
  41.     if ((cType & smcTypeMask) == smCharPunct)
  42.     {
  43.         cType &= smcClassMask;
  44.         if ((cType == smPunctNormal) || (cType == smPunctBlank))
  45.         {
  46.             return true;
  47.         }
  48.     }
  49.     return false;
  50. }
  51.  
  52. pascal void _WEIntelligentCut(SInt32 *rangeStart, SInt32 *rangeEnd, WEHandle hWE)
  53. {
  54.  
  55.     // _WEIntelligentCut is called by other WASTE routines to determine the actual
  56.     // range to be deleted when weFIntCutAndPaste is enabled.
  57.     // On entry, rangeStart and rangeEnd specify the selection range visible to the user.
  58.     // On exit, rangeStart and rangeEnd specify the actual range to be removed.
  59.  
  60.     // do nothing if the intelligent cut-and-paste feature is disabled
  61.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  62.     {
  63.         return;
  64.     }
  65.  
  66.     // intelling cut-&-paste rules should be applied only to word ranges...
  67.     if (!_WEIsWordRange(*rangeStart, *rangeEnd, hWE))
  68.     {
  69.         return;
  70.     }
  71.  
  72.     // ...without punctuation characters at the beginning or end
  73.     if (_WEIsPunct(*rangeStart, hWE))
  74.     {
  75.         return;
  76.     }
  77.     if (_WEIsPunct(*rangeEnd - 1, hWE))
  78.     {
  79.         return;
  80.     }
  81.  
  82.     // if the character preceding the selection range is a space, discard it
  83.     if (WEGetChar(*rangeStart - 1, hWE) == kSpace)
  84.     {
  85.         (*rangeStart)--;
  86.     }
  87.     // else, if the character following the selection range is a space, discard it
  88.     else if (WEGetChar(*rangeEnd, hWE) == kSpace)
  89.     {
  90.         (*rangeEnd)++;
  91.     }
  92. }
  93.  
  94. pascal SInt16 _WEIntelligentPaste(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  95. {
  96.     SInt16 retval;
  97.  
  98.     // _WEIntelligentPaste is called by other WASTE routines to determine whether
  99.     // an additional space character should be added (before or after) after inserting
  100.     // new text (usually from the Clipboard or from a drag).
  101.  
  102.     retval = weDontAddSpaces;
  103.  
  104.     // do nothing unless the intelligent cut-and-paste feature is enabled
  105.     if (!BTST((*hWE)->features, weFIntCutAndPaste))
  106.     {
  107.         return retval;
  108.     }
  109.  
  110.     // extra spaces will be added only if the pasted text looks like a word range,
  111.     // without punctuation characters at the beginning or at the end
  112.     if (_WEIsPunct(rangeStart, hWE))
  113.     {
  114.         return retval;
  115.     }
  116.     if (_WEIsPunct(rangeEnd - 1, hWE))
  117.     {
  118.         return retval;
  119.     }
  120.  
  121.     // if the character on the left of the pasted text is a punctuation character
  122.     // and the character on the right isn't,  add a space on the right, and vice versa
  123.     if (_WEIsPunct(rangeStart - 1, hWE))
  124.     {
  125.         if (!_WEIsPunct(rangeEnd, hWE))
  126.         {
  127.             retval = weAddSpaceOnRightSide;
  128.         }
  129.     }
  130.     else if (_WEIsPunct(rangeEnd, hWE))
  131.     {
  132.         retval = weAddSpaceOnLeftSide;
  133.     }
  134.  
  135.     return retval;
  136. }
  137.  
  138. pascal OSErr _WEInsertRun(SInt32 runIndex, SInt32 offset, SInt32 styleIndex, WEPtr pWE)
  139. {
  140.  
  141.     // Insert a new entry in the style run array, at the specified runIndex position.
  142.     // The new entry consists of the pair <offset, styleIndex>.
  143.  
  144.     WERunArrayEntry entry;
  145.     OSErr err;
  146.  
  147.     // prepare the entry record to be inserted in the array
  148.     entry.runStart = offset;
  149.     entry.styleIndex = styleIndex;
  150.  
  151.     // do the insertion
  152.     if ((err = _WESplice((Handle) pWE->hRuns, &entry, sizeof(entry), (runIndex + 1) * sizeof(entry))) != noErr)
  153.     {
  154.         return err;
  155.     }
  156.  
  157.     // increment style run count
  158.     pWE->nRuns++;
  159.  
  160.     // increment the reference count field of the style table entry
  161.     // referenced by the newly inserted style run
  162.     (*pWE->hStyles)[styleIndex].refCount++;
  163.  
  164.     return noErr;
  165. }
  166.  
  167. pascal void _WERemoveRun(SInt32 runIndex, WEPtr pWE)
  168. {
  169.     WEStyleTableEntry *pStyle;
  170.  
  171.     // get a pointer to the style table entry referenced by the style run
  172.     pStyle = *pWE->hStyles + (*pWE->hRuns)[runIndex].styleIndex;
  173.  
  174.     // decrement the reference count field of the style table entry
  175.     // referenced by the style run to be removed
  176.     pStyle->refCount--;
  177.  
  178. #if WASTE_OBJECTS
  179.     // dispose of the embedded object (if any)
  180.     if (pStyle->info.runStyle.tsObject != nil)
  181.     {
  182.         _WEFreeObject(pStyle->info.runStyle.tsObject);
  183.     }
  184. #endif
  185.  
  186.     // remove a "slot" from the run array
  187.     _WESplice((Handle) pWE->hRuns, nil, - sizeof(WERunArrayEntry), runIndex * sizeof(WERunArrayEntry));
  188.  
  189.     // decrement style run count
  190.     pWE->nRuns--;
  191. }
  192.  
  193. pascal void _WEChangeRun(SInt32 runIndex, SInt32 newStyleIndex, Boolean keepOld, WEPtr pWE)
  194. {
  195. #if !WASTE_OBJECTS
  196.     #pragma unused(keepOld)
  197. #endif
  198.     // change the styleIndex field of the specified entry of the style run array
  199.  
  200.     SInt32 oldStyleIndex;
  201.     WEStyleTableEntry *oldStyle, *newStyle;
  202.  
  203.     // do the change
  204.     oldStyleIndex = (*pWE->hRuns)[runIndex].styleIndex;
  205.     (*pWE->hRuns)[runIndex].styleIndex = newStyleIndex;
  206.  
  207.     // get pointers to old and new style table elements
  208.     oldStyle = *pWE->hStyles + oldStyleIndex;
  209.     newStyle = *pWE->hStyles + newStyleIndex;
  210.  
  211.     // increment the reference count field of the new style table entry
  212.     newStyle->refCount++;
  213.  
  214.     // decrement the reference count field of the old style table entry
  215.     oldStyle->refCount--;
  216.  
  217. #if WASTE_OBJECTS
  218.     // dispose of the embedded object (if any) unless it is again referenced in the new style
  219.     if (!keepOld)
  220.     {
  221.         WEObjectDescHandle oldObject = oldStyle->info.runStyle.tsObject;
  222.  
  223.         if ((oldObject != nil) && (oldObject != newStyle->info.runStyle.tsObject))
  224.         {
  225.             _WEFreeObject(oldObject);
  226.         }
  227.     }
  228. #endif
  229. }
  230.  
  231. pascal OSErr _WENewStyle(const WETextStyle *ts, SInt32 *styleIndex, WEPtr pWE)
  232. {
  233.     // given the specified WETextStyle record, find the corresponding entry
  234.     // in the style table (create a new entry if necessary), and return its index
  235.  
  236.     WEStyleTableEntry *pEntry;
  237.     WEStyleTableEntry entry;
  238.     SInt32 index;
  239.     SInt32 unusedIndex;
  240.     OSErr err;
  241.  
  242.     // see if the given style already exists in the style table
  243.     // while scanning the table, also remember the position of the first unused style, if any
  244.     unusedIndex = -1;
  245.     pEntry = *pWE->hStyles;
  246.     for ( index = 0 ; index < pWE->nStyles ; index++ )
  247.     {
  248.         // check for entries which aren't referenced and can be recycled
  249.         if (pEntry->refCount == 0)
  250.         {
  251.             unusedIndex = index;
  252.         }
  253.  
  254.         // perform a bitwise comparison between the current entry and the specified style
  255.         // (actually, we ignore metrics information)
  256.         else if (_WEBlockCmp(&pEntry->info.runStyle, ts, sizeof(WETextStyle)))
  257.         {
  258.             // found: style already present
  259.             *styleIndex = index;
  260.             return noErr;
  261.         }
  262.         pEntry++;
  263.     } // for
  264.  
  265.     // the specified style doesn't exist in the style table
  266.     // since this is a new entry, we have to calculate font metrics information
  267.     entry.info.runStyle = *ts;
  268.     _WEFillFontInfo(pWE->port, &entry.info);
  269.  
  270.     // see if we can recycle an unused entry
  271.     if (unusedIndex >= 0)
  272.     {
  273.         index = unusedIndex;
  274.         (*pWE->hStyles)[index].info = entry.info;
  275.     }
  276.     else
  277.     {
  278.         // no reusable entry: we have to append a new entry at the end of the table
  279.         entry.refCount = 0;
  280.         if ((err = _WESplice((Handle) pWE->hStyles, &entry, sizeof(entry), -1)) != noErr)
  281.         {
  282.             return err;
  283.         }
  284.  
  285.         // update style count in the WE record
  286.         pWE->nStyles++;
  287.     }
  288.  
  289.     // return the index to the new entry
  290.     *styleIndex = index;
  291.     return noErr;
  292. }
  293.  
  294. pascal OSErr _WERedraw(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  295. {
  296.     WEPtr pWE = *hWE;        // assume WE record is already locked
  297.     WELineRec *pLines;
  298.     SInt32 startLine, endLine;
  299.     SInt32 oldTextHeight, newTextHeight;
  300.     LongRect r;
  301.     Rect viewRect, updateRect;
  302.     RgnHandle saveClip, auxRgn;
  303.     GrafPtr savePort;
  304.     OSErr err;
  305.  
  306.     // do nothing if recalculation has been inhibited
  307.     if (!BTST(pWE->features, weFInhibitRecal))
  308.     {
  309.         // hide the caret
  310.         if (BTST(pWE->flags, weFCaretVisible))
  311.         {
  312.             _WEBlinkCaret(hWE);
  313.         }
  314.  
  315.         // remember total text height
  316.         oldTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  317.  
  318.         // find line range affected by modification
  319.         startLine = WEOffsetToLine(rangeStart, hWE);
  320.         endLine = WEOffsetToLine(rangeEnd, hWE);
  321.  
  322.         // recalculate line breaks starting from startLine
  323.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) != noErr)
  324.         {
  325.             goto cleanup;
  326.         }
  327.  
  328.         // recalculate slops
  329.         _WERecalSlops(startLine, endLine, hWE);
  330.  
  331.         // do nothing if redrawing has been inhibited
  332.         if (!BTST(pWE->features, weFInhibitRedraw))
  333.         {
  334.             // calculate new total text height
  335.             newTextHeight = pWE->destRect.bottom - pWE->destRect.top;
  336.  
  337.             // calculate the rectangle to redraw (in long coordinates)
  338.             r.left = -SHRT_MAX;
  339.             r.right = SHRT_MAX;
  340.             pLines = *pWE->hLines;
  341.             r.top = pLines[startLine].lineOrigin;
  342.  
  343.             // if total text height hasn't changed, it's enough to redraw lines up to endLine
  344.             // otherwise we must redraw all lines from startLine on
  345.             if ((newTextHeight == oldTextHeight) && (endLine < pWE->nLines - 1))
  346.             {
  347.                 r.bottom = pLines[endLine + 1].lineOrigin;
  348.             }
  349.             else if (newTextHeight < oldTextHeight)
  350.             {
  351.                 r.bottom = oldTextHeight;
  352.             }
  353.             else
  354.             {
  355.                 r.bottom = newTextHeight;
  356.             }
  357.  
  358.             WEOffsetLongRect(&r, 0, pWE->destRect.top);
  359.  
  360.             // calculate the intersection between this rectangle and the view rectangle
  361.             WELongRectToRect(&r, &updateRect);
  362.             WELongRectToRect(&pWE->viewRect, &viewRect);
  363.  
  364.             if (SectRect(&updateRect, &viewRect, &updateRect))
  365.             {
  366.                 // set up the port and the clip region
  367.                 GetPort(&savePort);
  368.                 SetPort(pWE->port);
  369.  
  370.                 // restrict the clip region to updateRect
  371.                 saveClip = NewRgn();
  372.                 GetClip(saveClip);
  373.                 auxRgn = NewRgn();
  374.                 RectRgn(auxRgn, &updateRect);
  375.                 SectRgn(saveClip, auxRgn, auxRgn);
  376.                 SetClip(auxRgn);
  377.                 DisposeRgn(auxRgn);
  378.  
  379.                 // we only really need to redraw the visible lines
  380.                 startLine = _WEPixelToLine(updateRect.top - pWE->destRect.top, hWE);
  381.                 endLine = _WEPixelToLine(updateRect.bottom - pWE->destRect.top - 1, hWE);
  382.  
  383.                 // redraw the lines (pass true in the doErase parameter)
  384.                 _WEDrawLines(startLine, endLine, true, hWE);
  385.  
  386.                 // erase the portion of the update rectangle below the last line (if any)
  387.                 pLines = *pWE->hLines;
  388.                 updateRect.top = pWE->destRect.top + pLines[endLine + 1].lineOrigin;
  389.                 if (updateRect.top < updateRect.bottom)
  390.                 {
  391.                     CallWEEraseProc(&updateRect, hWE, pWE->eraseHook);
  392.                 }
  393.  
  394.                 // restore the clip region
  395.                 SetClip(saveClip);
  396.                 DisposeRgn(saveClip);
  397.  
  398.                 // restore the port
  399.                 SetPort(savePort);
  400.  
  401.                 // redraw the caret or the selection range
  402.                 if (pWE->selStart < pWE->selEnd)
  403.                 {
  404.                     _WEHiliteRange(pWE->selStart, pWE->selEnd, hWE);
  405.                 }
  406.                 else
  407.                 {
  408.                     _WEBlinkCaret(hWE);
  409.                 }
  410.             } // if SectRect
  411.  
  412.             // scroll the selection range into view
  413.             WESelView(hWE);
  414.  
  415.         } // if redraw not inhibited
  416.     } // if recal not inhibited
  417.  
  418.     // clear result code
  419.     err = noErr;
  420.  
  421. cleanup:
  422.     // return result code
  423.     return err;
  424. }
  425.  
  426. pascal OSErr WECalText(WEHandle hWE)
  427. {
  428.     Boolean saveWELock;
  429.     OSErr err;
  430.  
  431.     // lock WE record
  432.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  433.  
  434. #if WASTE_WECALTEXT_DOES_REDRAW
  435.  
  436.     // recalculate line breaks & slops and redraw the text
  437.     err = _WERedraw(0, LONG_MAX, hWE);
  438.  
  439. #else
  440.  
  441.     {
  442.         SInt32 startLine = 0;
  443.         SInt32 endLine = LONG_MAX;
  444.  
  445.         // recalculate line breaks & slops without redrawing anything
  446.         if ((err = _WERecalBreaks(&startLine, &endLine, hWE)) == noErr)
  447.         {
  448.             _WERecalSlops(startLine, endLine, hWE);
  449.         }
  450.     }
  451.  
  452. #endif
  453.  
  454.     // unlock the WE record
  455.     _WESetHandleLock((Handle) hWE, saveWELock);
  456.  
  457.     // return result code
  458.     return err;
  459. }
  460.  
  461. pascal OSErr _WESetStyleRange(SInt32 rangeStart, SInt32 rangeEnd, WEStyleMode mode, const WETextStyle *ts, WEHandle hWE)
  462. {
  463.     // alter the style attributes of the specified text range according to ts and mode
  464.  
  465.     WEPtr pWE = *hWE;                    // assume WE record is already locked
  466.     WERunArrayHandle hRuns = pWE->hRuns;
  467.     SInt32 offset;
  468.     SInt32 runIndex;
  469.     SInt32 oldStyleIndex, newStyleIndex;
  470.     WERunInfo runInfo;
  471.     Style continuousStyles;
  472.     OSErr err;
  473.  
  474.     WEASSERT(((rangeStart < rangeEnd) || ((rangeStart == 0) && (rangeEnd == 0) && (pWE->textLength == 0))), "\pBad style range");
  475.  
  476.     // if mode contains weDoToggleFace, we need to determine which Quickdraw styles
  477.     // are continuous over the specified text range: those styles must be turned off
  478.     if (BTST(mode, kModeToggleFace))
  479.     {
  480.         WEStyleMode temp = weDoFace;
  481.         _WEContinuousStyleRange(rangeStart, rangeEnd, &temp, &runInfo.runAttrs.runStyle, hWE);
  482.         continuousStyles = runInfo.runAttrs.runStyle.tsFace;
  483.     }
  484.     else
  485.     {
  486.         continuousStyles = normal;
  487.     }
  488.  
  489.     // find the index to the first style run in the specified range
  490.     offset = rangeStart;
  491.     runIndex = WEOffsetToRun(offset, hWE);
  492.  
  493.     // run thru all the style runs that encompass the selection range
  494.     do
  495.     {
  496.         // find style index for this run and retrieve corresponding style attributes
  497.         oldStyleIndex = (*hRuns)[runIndex].styleIndex;
  498.         _WEGetIndStyle(runIndex, &runInfo, hWE);
  499.  
  500.         // _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run:
  501.         // correct this anomaly (which is useful for other purposes, anyway)
  502.         if (runInfo.runEnd > pWE->textLength)
  503.         {
  504.             runInfo.runEnd = pWE->textLength;
  505.         }
  506.  
  507.         // apply changes to existing style attributes as requested
  508.         _WECopyStyle(ts, &runInfo.runAttrs.runStyle, continuousStyles, mode);
  509.  
  510.         // the high bit of tsFlags must be set if the font
  511.         // belongs to a right-to-left script system
  512.         if (BTST(pWE->flags, weFBidirectional))
  513.         {
  514.             if (GetScriptVariable(FontToScript(runInfo.runAttrs.runStyle.tsFont), smScriptRight) != 0)
  515.             {
  516.                 BSET(runInfo.runAttrs.runStyle.tsFlags, tsRightToLeft);
  517.             }
  518.         }
  519.  
  520.         // get a style index for the new text style
  521.         if ((err = _WENewStyle(&runInfo.runAttrs.runStyle, &newStyleIndex, pWE)) != noErr)
  522.         {
  523.             goto cleanup;
  524.         }
  525.  
  526.         // if offset falls on a style boundary and this style run has become identical
  527.         // to the previous one, merge the two runs together
  528.         if ((offset == runInfo.runStart) && (runIndex > 0) &&
  529.             ((*hRuns)[runIndex - 1].styleIndex == newStyleIndex))
  530.         {
  531.             _WERemoveRun(runIndex, pWE);
  532.             runIndex--;
  533.         }
  534.  
  535.         // style index changed?
  536.         if (oldStyleIndex != newStyleIndex)
  537.         {
  538.             // if offset is in the middle of a style run, insert a new style run in the run array
  539.             if (offset > runInfo.runStart)
  540.             {
  541.                 if ((err = _WEInsertRun(runIndex, offset, newStyleIndex, pWE)) != noErr)
  542.                 {
  543.                     goto cleanup;
  544.                 }
  545.                 runIndex++;
  546.             }
  547.             else
  548.             {
  549.                 // otherwise just change the styleIndex field of the current style run entry
  550.                 _WEChangeRun(runIndex, newStyleIndex, (rangeEnd < runInfo.runEnd), pWE);
  551.             }
  552.  
  553.             // if specified range ends in the middle of a style run, insert yet another entry
  554.             if (rangeEnd < runInfo.runEnd)
  555.             {
  556.                 if ((err = _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE)) != noErr)
  557.                 {
  558.                     goto cleanup;
  559.                 }
  560.             }
  561.         } // if oldStyle != newStyle
  562.  
  563.         // go to next style run
  564.         runIndex++;
  565.         offset = runInfo.runEnd;
  566.  
  567.     } while (offset < rangeEnd);
  568.  
  569.     // if the last style run ends exactly at the end of the specified range,
  570.     // see if we can merge it with the following style run
  571.     if ((offset == rangeEnd) && (runIndex < pWE->nRuns) &&
  572.         ((*hRuns)[runIndex].styleIndex == newStyleIndex))
  573.     {
  574.         _WERemoveRun(runIndex, pWE);
  575.     }
  576.  
  577.     // clear result code
  578.     err = noErr;
  579.  
  580. cleanup:
  581.     // return result code
  582.     return err;
  583. }
  584.  
  585. pascal OSErr _WEApplyStyleScrap(SInt32 rangeStart, SInt32 rangeEnd, StScrpHandle styleScrap, WEHandle hWE)
  586. {
  587.     // apply the given style scrap to the specified text range
  588.  
  589.     TEStyleScrapElement *pEntry;
  590.     Size styleScrapSize;
  591.     SInt32 runStart, runEnd;
  592.     SInt32 entryCount, index;
  593.     WETextStyle ts;
  594.     OSErr err;
  595.  
  596.     // sanity check: make sure the style scrap handle is a reasonable size
  597.     err = weCorruptDataErr;
  598.     styleScrapSize = GetHandleSize((Handle) styleScrap) - sizeof(SInt16);
  599.     if (styleScrapSize < 0)
  600.     {
  601.         goto cleanup;        // too short: header is missing
  602.     }
  603.  
  604.     // calculate entry count based on handle size
  605.     entryCount = styleScrapSize / sizeof(ScrpSTElement);
  606.     if (styleScrapSize != entryCount * sizeof(ScrpSTElement))
  607.     {
  608.         goto cleanup;        // not an integral number of entries
  609.     }
  610.  
  611.     // the calculated entry count must match scrpNStyles,
  612.     // unless it's larger than 32,766
  613.     if (entryCount < SHRT_MAX)
  614.     {
  615.         if (entryCount != (*styleScrap)->scrpNStyles)
  616.         {
  617.             goto cleanup;    // invalid entry count
  618.         }
  619.     }
  620.  
  621.     // loop through each entry of the style scrap
  622.     for ( index = 0; index < entryCount; index++ )
  623.     {
  624.         // get a pointer to the current scrap entry
  625.         pEntry = (TEStyleScrapElement *) ((*styleScrap)->scrpStyleTab + index);
  626.  
  627.         // calculate text run to which this entry is to be applied
  628.         runStart = rangeStart + pEntry->scrpStartChar;
  629.         if (index < entryCount - 1)
  630.         {
  631.             runEnd = rangeStart + pEntry[1].scrpStartChar;
  632.         }
  633.         else
  634.         {
  635.             runEnd = rangeEnd;
  636.         }
  637.  
  638.         // perform some range checking
  639.         if (runEnd > rangeEnd)
  640.         {
  641.             runEnd = rangeEnd;
  642.         }
  643.         if (runStart >= runEnd)
  644.         {
  645.             continue;
  646.         }
  647.  
  648.         // copy style to a local variable in case memory moves
  649.         * (TextStyle *) &ts = pEntry->scrpTEAttrs.runTEStyle;
  650.  
  651.         // apply the specified style to the range
  652.         if ((err = _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, &ts, hWE)) != noErr)
  653.         {
  654.             goto cleanup;
  655.         }
  656.     }
  657.  
  658.     // clear result code
  659.     err = noErr;
  660.  
  661. cleanup:
  662.     // return result code
  663.     return err;
  664. }
  665.  
  666. #if WASTE_OBJECTS
  667.  
  668. pascal OSErr _WEApplySoup(SInt32 offset, Handle hSoup, WEHandle hWE)
  669. {
  670.     WESoup soup;
  671.     Ptr pSoup, pSoupEnd;
  672.     WETextStyle ts;
  673.     Handle hObjectData;
  674.     SInt32 objectOffset;
  675.     Boolean saveWELock;
  676.     OSErr err;
  677.  
  678.     BLOCK_CLR(ts);
  679.     hObjectData = nil;
  680.  
  681.     // lock the WE record
  682.     saveWELock = _WESetHandleLock((Handle) hWE, true);
  683.  
  684.     // lock the soup in high heap
  685.     HLockHi(hSoup);
  686.     pSoup = *hSoup;
  687.     pSoupEnd = pSoup + GetHandleSize(hSoup);
  688.  
  689.     // loop through each object descriptor in the soup
  690.     while (pSoup < pSoupEnd)
  691.     {
  692.         // Object descriptors may be aligned to odd addresses (duh!)
  693.         // this isn't a problem on 68020+ and PPC CPUs, but causes
  694.         // a fatal address error on the 68000.  To avoid this, we
  695.         // copy the descriptor to the stack with BlockMoveData()
  696.         // before trying to access its fields.
  697.         BlockMoveData(pSoup, &soup, sizeof(soup));
  698.  
  699.         // if soupDataSize is negative, this soup is a special type that we won't handle here
  700.         if (soup.soupDataSize < 0)
  701.         {
  702.             continue;
  703.         }
  704.  
  705.         // create a new relocatable block the hold the object data
  706.         if ((err = _WEAllocate(soup.soupDataSize, kAllocTemp, &hObjectData)) != noErr)
  707.         {
  708.             goto cleanup;
  709.         }
  710.  
  711.         // copy the object data to this block
  712.         BlockMoveData(pSoup + sizeof(soup), *hObjectData, soup.soupDataSize);
  713.  
  714.         // create a new object out of the tagged data
  715.         if ((err = _WENewObject(soup.soupType, hObjectData, hWE, &ts.tsObject)) != noErr)
  716.         {
  717.             goto cleanup;
  718.         }
  719.  
  720.         // if there was no new handler for this object, use the object size stored in the soup
  721.         if ((*ts.tsObject)->objectTable == nil)
  722.         {
  723.             (*ts.tsObject)->objectSize = soup.soupSize;
  724.         }
  725.  
  726.         // record a reference to the object descriptor in the style table
  727.         objectOffset = soup.soupOffset + offset;
  728.         err = _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, &ts, hWE);
  729.         hObjectData = nil;
  730.         ts.tsObject = nil;
  731.         if (err != noErr)
  732.         {
  733.             goto cleanup;
  734.         }
  735.  
  736.         // advance soup pointer
  737.         pSoup += sizeof(soup) + soup.soupDataSize;
  738.  
  739.     } // while
  740.  
  741.     // clear result code
  742.     err = noErr;
  743.  
  744. cleanup:
  745.     // clean up
  746.     HUnlock(hSoup);
  747.     _WEForgetHandle((Handle *) &ts.tsObject);
  748.     _WEForgetHandle(&hObjectData);
  749.  
  750.     // unlock the WE record
  751.     _WESetHandleLock((Handle) hWE, saveWELock);
  752.  
  753.     // return result code
  754.     return err;
  755. }
  756.  
  757. #endif    // WASTE_OBJECTS
  758.  
  759. pascal void _WEBumpRunStart(SInt32 runIndex, SInt32 deltaRunStart, WEPtr pWE)
  760. {
  761.     // add deltaLineStart to the lineStart field of all line records
  762.     // starting from lineIndex
  763.  
  764.     WERunArrayEntry *pRun = *pWE->hRuns + runIndex;
  765.     SInt32 nRuns = pWE->nRuns;
  766.  
  767.     // loop through the style run array adjusting the runStart fields
  768.     for ( ; runIndex <= nRuns; runIndex++ )
  769.     {
  770.         pRun->runStart += deltaRunStart;
  771.         pRun++;
  772.     }
  773. }
  774.  
  775. pascal void _WERemoveRunRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  776. {
  777.     // the range of text between rangeStart and rangeEnd is being deleted
  778.     // update the style run array (and the style table) accordingly
  779.  
  780.     WEPtr pWE = *hWE;    // assume WE record is already locked
  781.     WERunArrayEntry *pRun;
  782.     SInt32 startRun, endRun;
  783.  
  784.     // find the index to the first and last style runs in the specified range
  785.     startRun = WEOffsetToRun(rangeStart, hWE);
  786.     endRun = WEOffsetToRun(rangeEnd, hWE) - 1;
  787.  
  788.     // remove all style runs between startRun and endRun
  789.     for ( ; endRun > startRun; endRun-- )
  790.     {
  791.         _WERemoveRun(endRun, pWE);
  792.     }
  793.  
  794.     // move back all subsequent style runs
  795.     _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  796.  
  797.     if ((endRun == startRun) && (endRun < pWE->nRuns - 1))
  798.     {
  799.         pRun = *pWE->hRuns + endRun;
  800.         pRun[1].runStart = rangeStart;
  801.     }
  802.  
  803.     // remove the first style run if is has become zero length
  804.     pRun = *pWE->hRuns + startRun;
  805.     if (pRun[0].runStart == pRun[1].runStart)
  806.     {
  807.         _WERemoveRun(startRun, pWE);
  808.         startRun--;
  809.     }
  810.  
  811.     // merge the first and last runs if they have the same style index
  812.     if (startRun >= 0)
  813.     {
  814.         pRun = *pWE->hRuns + startRun;
  815.         if (pRun[0].styleIndex == pRun[1].styleIndex)
  816.         {
  817.             _WERemoveRun(startRun + 1, pWE);
  818.         }
  819.     }
  820. }
  821.  
  822. pascal void _WEBumpLineStart(SInt32 lineIndex, SInt32 deltaLineStart, WEPtr pWE)
  823. {
  824.     // add deltaLineStart to the lineStart field of all line records
  825.     // starting from lineIndex
  826.  
  827.     WELineRec *pLine = *pWE->hLines + lineIndex;
  828.     SInt32 nLines = pWE->nLines;
  829.  
  830.     // loop through the line array adjusting the lineStart fields
  831.     for ( ; lineIndex <= nLines; lineIndex++ )
  832.     {
  833.         pLine->lineStart += deltaLineStart;
  834.         pLine++;
  835.     }
  836. }
  837.  
  838. pascal void _WERemoveLineRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  839. {
  840.     // the range of text between rangeStart and rangeEnd is being deleted
  841.     // update the line array accordingly
  842.  
  843.     WEPtr pWE = *hWE;    // assume WE record is already locked
  844.     SInt32 startLine, nLines;
  845.  
  846.     // remove all line records between rangeStart and rangeEnd
  847.     startLine = WEOffsetToLine(rangeStart, hWE) + 1;
  848.     nLines = WEOffsetToLine(rangeEnd, hWE) - startLine + 1;
  849.  
  850.     _WESplice((Handle) pWE->hLines, nil, - (nLines * sizeof(WELineRec)), startLine * sizeof(WELineRec));
  851.     pWE->nLines -= nLines;
  852.  
  853.     // update the lineStart field of all the line records that follow
  854.     _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  855. }
  856.  
  857. pascal OSErr _WEDeleteRange(SInt32 rangeStart, SInt32 rangeEnd, WEHandle hWE)
  858. {
  859.     // used internally to delete a text range
  860.     WEPtr pWE = *hWE;    // assume WE record is already locked
  861.     WERunInfo runInfo;
  862.     SInt32 delta;
  863.     OSErr err = noErr;
  864.  
  865.     if (rangeEnd > pWE->textLength)
  866.     {
  867.         rangeEnd = pWE->textLength;
  868.     }
  869.  
  870.     // do nothing if the specified range is empty
  871.     if ((delta = (rangeStart - rangeEnd)) == 0)
  872.     {
  873.         goto cleanup;
  874.     }
  875.  
  876.     // save the first style in the specified range in nullStyle
  877.     WEGetRunInfo(rangeStart, &runInfo, hWE);
  878.     pWE->nullStyle = runInfo.runAttrs;
  879.     BSET(pWE->flags, weFUseNullStyle);
  880.  
  881. #if WASTE_OBJECTS
  882.     // special case: if we're deleting up to the end of the text, see whether
  883.     // there's an embedded object at the very end and remove it
  884.     if (rangeEnd == pWE->textLength)
  885.     {
  886.         WEGetRunInfo(rangeEnd - 1, &runInfo, hWE);
  887.         if (runInfo.runAttrs.runStyle.tsObject != nil)
  888.         {
  889.             runInfo.runAttrs.runStyle.tsObject = nil;
  890.             if ((err = _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, &runInfo.runAttrs.runStyle, hWE)) != noErr)
  891.             {
  892.                 goto cleanup;
  893.             }
  894.         }
  895.     }
  896. #endif
  897.  
  898.     // remove all line records between rangeStart and rangeEnd
  899.     _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  900.  
  901.     // remove all style runs between rangeStart and rangeEnd
  902.     _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  903.  
  904.     // remove the text
  905.     if ((err = _WESplice(pWE->hText, nil, delta, rangeStart)) != noErr)
  906.     {
  907.         goto cleanup;
  908.     }
  909.  
  910.     // update textLength field
  911.     pWE->textLength += delta;
  912.  
  913.     // we modified the text, so the anchor range (if any) is no longer valid
  914.     pWE->clickCount = 0;
  915.  
  916.     // call the flux callback, if any
  917.     if (pWE->fluxProc != nil)
  918.     {
  919.         CallWEFluxProc(rangeStart, delta, hWE, pWE->fluxProc);
  920.     }
  921.  
  922. cleanup:
  923.     // return result code
  924.     return err;
  925. }
  926.  
  927. pascal OSErr _WEInsertText(SInt32 offset, Ptr textPtr, SInt32 textLength, WEHandle hWE)
  928. {
  929.     WEPtr pWE = *hWE;    // assume WE record is already locked
  930.     WEStyleMode mode;
  931.     OSErr err = noErr;
  932.  
  933.     // do nothing if textLength is zero or negative
  934.     if (textLength <= 0)
  935.     {
  936.         goto cleanup;
  937.     }
  938.  
  939.     // insert the text
  940.     if ((err = _WESplice(pWE->hText, textPtr, textLength, offset)) != noErr)
  941.     {
  942.         goto cleanup;
  943.     }
  944.  
  945.     // update the lineStart fields of all lines following the insertion point
  946.     _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  947.  
  948.     // update the runStart fields of all style runs following the insertion point
  949.     _WEBumpRunStart(WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  950.  
  951.     // update the textLength field
  952.     pWE->textLength += textLength;
  953.  
  954.     // we modified the text, so the anchor range (if any) is no longer valid
  955.     pWE->clickCount = 0;
  956.  
  957.     // make sure the newly inserted text doesn't reference any embedded object
  958. #if WASTE_OBJECTS
  959.     pWE->nullStyle.runStyle.tsObject = nil;
  960. #endif
  961.     mode = weDoObject;
  962.  
  963.     // if there is a valid null style, apply it to the newly inserted text
  964.     if (BTST(pWE->flags, weFUseNullStyle))
  965.     {
  966.         mode += (weDoAll + weDoReplaceFace);
  967.     }
  968.  
  969.     if ((err = _WESetStyleRange(offset, offset + textLength, mode, &pWE->nullStyle.runStyle, hWE)) != noErr)
  970.     {
  971.         goto cleanup;
  972.     }
  973.  
  974.     // call the flux callback, if any
  975.     if (pWE->fluxProc != nil)
  976.     {
  977.         CallWEFluxProc(offset, textLength, hWE, pWE->fluxProc);
  978.     }
  979.  
  980. cleanup:
  981.     return err;
  982. }
  983.